#include "StringHandle.h"
namespace re{
namespace string{

static boost::mutex g_mutex;
static std::tr1::shared_ptr<StringTable> g_StringTable;

StringHandle::StringHandle(const StringHandle& handle)
{
	m_id = handle.m_id;
	StringTable::Get()->AddReference(m_id);
}
StringHandle::StringHandle(const unsigned int id)
{
	m_id = id;
	StringTable::Get()->AddReference(m_id);
}
StringHandle::StringHandle(const std::string & str)
{
	m_id = StringTable::Get()->GetIdByString(str);
	StringTable::Get()->AddReference(m_id);
}
StringHandle::StringHandle(const char * const str)
{
	m_id = StringTable::Get()->GetIdByString(str);
	StringTable::Get()->AddReference(m_id);
}
StringHandle::StringHandle(const std::wstring & str)
{
	std::string str_char;
	str_char.append(str.begin(), str.end());
	m_id = StringTable::Get()->GetIdByString(str_char);
	StringTable::Get()->AddReference(m_id);
}
StringHandle::StringHandle(const TCHAR * const str)
{
	std::string str_char; std::wstring wstr = str;
	str_char.append(wstr.begin(), wstr.end());
	m_id = StringTable::Get()->GetIdByString(str_char);
	StringTable::Get()->AddReference(m_id);
}
StringHandle::~StringHandle()
{
	StringTable::Get()->SubReference(m_id);
}

StringHandle& StringHandle::operator=(const StringHandle& handle)
{
	if(m_id != handle.m_id)
	{
		StringTable::Get()->SubReference(m_id);
		m_id = handle.m_id;
	}
	StringTable::Get()->AddReference(m_id);
	return *this;
}
bool StringHandle::operator<(const StringHandle& handle) const
{
	if(m_id < handle.m_id)
	{
		return true;
	}
	return false;
}
bool StringHandle::operator==(const StringHandle& handle) const
{
	if(m_id == handle.m_id)
	{
		return true;
	}
	return false;
}


unsigned int StringTable::GetIdByString(const std::string& str)
{
	boost::mutex::scoped_lock lock(m_mutex);
#ifdef HIGH_SPEED_STRINGS
	StringMap::iterator i = m_StringMap.find(str);
	if(i != m_StringMap.end())
	{
		return i->second->first;
	}
#else
	IdMap::iterator it = m_IdMap.begin();
	IdMap::iterator end = m_IdMap.end();
	while(it != end)
	{
		if((*it).second.second == str)
		{
			return it->first;
		}
		++it;
	}
#endif
	unsigned int id = get_a_new_id();
	m_IdMap[id] = std::make_pair(0,str);

#ifdef HIGH_SPEED_STRINGS
	m_StringMap[str] = m_IdMap.find(id);
#endif

	return id;
}
unsigned int StringTable::ValidateId(const unsigned int id)
{
	boost::mutex::scoped_lock lock(m_mutex);
	IdMap::iterator i = m_IdMap.find(id);
	if(i != m_IdMap.end())
	{
		return id;
	}
	return 0;
}
const std::string * const StringTable::GetStringById(const unsigned int id)
{
	if(id == 0)
	{
		return 0;
	}
	boost::mutex::scoped_lock lock(m_mutex);
	IdMap::iterator i = m_IdMap.find(id);
	if(i == m_IdMap.end())
	{
		return 0;
	}
	return &i->second.second;
}
unsigned int StringTable::get_a_new_id()
{
	if(m_free_ids == 0)
	{
		return ++m_last_id;
	}
	--m_free_ids;
	IdMap::iterator i = m_IdMap.begin();
	IdMap::iterator end = m_IdMap.end();
	unsigned int current_id = 1;
	while(i != end)
	{
		if(current_id != (*i).first)
		{
			return current_id;
		}
		++current_id;
		++i;
	}
	return 0;
}
void StringTable::AddReference(const unsigned int id)
{
	if(id == 0)
	{
		return;
	}
	boost::mutex::scoped_lock lock(m_mutex);
	IdMap::iterator i = m_IdMap.find(id);
	if(i == m_IdMap.end())
	{
		return;
	}
	++i->second.first;
}
void StringTable::SubReference(const unsigned int id)
{
	if(id == 0)
	{
		return;
	}
	boost::mutex::scoped_lock lock(m_mutex);
	IdMap::iterator i = m_IdMap.find(id);
	if(i == m_IdMap.end())
	{
		return;
	}
	--i->second.first;
	if(i->second.first == 0)
	{
#ifdef HIGH_SPEED_STRINGS
		m_StringMap.erase(i->second.second);
#endif
		m_IdMap.erase(i);
	}
}

void StringTable::ReleaseStrings()
{
	boost::mutex::scoped_lock lock(m_mutex);
	m_IdMap.clear();
	m_StringMap.clear();
}

StringTable* const StringTable::Get()
{
	if(g_StringTable == std::tr1::shared_ptr<StringTable>())
	{
		boost::mutex::scoped_lock lock(g_mutex);
		if(g_StringTable == std::tr1::shared_ptr<StringTable>())
		{
			std::tr1::shared_ptr<StringTable> tb = std::tr1::shared_ptr<StringTable>(new StringTable);
			g_StringTable = tb;
		}
	}
	return g_StringTable.get();
}


}
}